package com.iacrqq.sedaf.application.config.xml;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.iacrqq.sedaf.application.config.ApplicationDefinition;
import com.iacrqq.sedaf.application.config.ApplicationParser;
import com.iacrqq.sedaf.application.config.DispatcherDefinition;
import com.iacrqq.sedaf.application.config.Property;
import com.iacrqq.sedaf.application.config.QueueDefinition;
import com.iacrqq.sedaf.application.config.RouterEntryDefinition;
import com.iacrqq.sedaf.application.config.StageDefinition;

/**
 * 
 * @author sihai
 *
 */
public class XMLApplicationParser extends DefaultHandler implements ApplicationParser {
	
	private static final String APPLICATION = "application";
	private static final String QUEUE = "queue";
	private static final String STAGE = "stage";
	private static final String PROPERTY = "property";
	private static final String VALUE = "value";
	private static final String EVENT_DISPATCHER = "dispatcher";
	private static final String ROUTER_ENTRY = "routerEntry";
	
	private static final String ATTRIBUTE_ID = "id";
	private static final String ATTRIBUTE_NAME = "name";
	private static final String ATTRIBUTE_VALUE = "value";
	
	private static final String ROUTER_ENTRY_ATTRIBUTE_STATE = "state";
	private static final String ROUTER_ENTRY_ATTRIBUTE_STAGE = "stage";
	
	private ThreadLocal<ApplicationDefinition> currentApplication = new ThreadLocal<ApplicationDefinition>();
	private ThreadLocal<QueueDefinition> currentQueue = new ThreadLocal<QueueDefinition>();
	private ThreadLocal<StageDefinition> currentStage = new ThreadLocal<StageDefinition>();
	private ThreadLocal<Property> currentProperty = new ThreadLocal<Property>();
	private ThreadLocal<DispatcherDefinition> currentDispatcher = new ThreadLocal<DispatcherDefinition>();
	//private ThreadLocal<RouterEntryDefinition> currentRouterEntry = new ThreadLocal<RouterEntryDefinition>();
	
	@Override
	public ApplicationDefinition parse(String fileName) {
		try
		{
			
			SAXParserFactory sf = SAXParserFactory.newInstance();
			SAXParser parser = sf.newSAXParser();
			parser.parse(new File(fileName), this);

			ApplicationDefinition appDefinition = currentApplication.get();
			
			currentApplication.remove();
			currentQueue.remove();
			currentStage.remove();
			currentProperty.remove();
			
			return appDefinition;
		}
		catch(SAXException e)
		{
			throw new RuntimeException(e);
		}
		catch(ParserConfigurationException e)
		{
			throw new RuntimeException(e);
		}
		catch(IOException e)
		{
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		super.startElement(uri, localName, qName, attributes);
		if(APPLICATION.equals(qName))
		{
			_parse_application_(attributes);
		}
		else if(QUEUE.equals(qName))
		{
			_parse_queue_(attributes);
		}
		else if(STAGE.equals(qName))
		{
			_parse_stage_(attributes);
		}
		else if(PROPERTY.equals(qName))
		{
			_parse_property_(attributes);
		}
		else if(VALUE.equals(qName))
		{
			_parse_value_(attributes);
		}
		else if(EVENT_DISPATCHER.equals(qName))
		{
			_parse_dispatcher_(attributes);
		}
		else if(ROUTER_ENTRY.equals(qName))
		{
			_parse_router_entry_(attributes);
		}
		else
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		//System.out.println("localName = " + localName + ", qName = " + qName);
	}
	
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		super.endElement(uri, localName, qName);
		if(APPLICATION.equals(qName))
		{
			
		}
		else if(QUEUE.equals(qName))
		{
			currentApplication.get().addQueueDefinition(currentQueue.get());
			currentQueue.remove();
		}
		else if(STAGE.equals(qName))
		{
			currentApplication.get().addStageDefinition(currentStage.get());
			currentStage.remove();
		}
		else if(PROPERTY.equals(qName))
		{
			QueueDefinition queue = currentQueue.get();
			StageDefinition stage = currentStage.get();
			if(queue != null && stage != null)
			{
				throw new RuntimeException("Wrong application config file.");
			}
			
			if(queue != null)
			{
				queue.addProperty(currentProperty.get());
			}
			
			if(stage != null)
			{
				stage.addProperty(currentProperty.get());
			}
			
			currentProperty.remove();
		}
		else if(VALUE.equals(qName))
		{
			
		}
		else if(EVENT_DISPATCHER.equals(qName))
		{
			if(currentApplication.get() == null)
			{
				throw new RuntimeException("Wrong application config file.");
			}
			
			currentApplication.get().setDispatcherDefinition(currentDispatcher.get());
		}
		else if(ROUTER_ENTRY.equals(qName))
		{
			
		}
		else
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		//System.out.println("localName = " + localName + ", qName = " + qName);
	}

	private void _parse_application_(Attributes attributes)
	{
		if(currentApplication.get() != null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		ApplicationDefinition application = new ApplicationDefinition();
		application.setName(attributes.getValue(ATTRIBUTE_NAME));
		currentApplication.set(application);
	}
	
	private void _parse_queue_(Attributes attributes)
	{
		if(currentQueue.get() != null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		QueueDefinition queueDefinition = new QueueDefinition();
		queueDefinition.setName(attributes.getValue(ATTRIBUTE_NAME));
		
		currentQueue.set(queueDefinition);
	}
	
	private void _parse_stage_(Attributes attributes)
	{
		if(currentStage.get() != null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		StageDefinition stageDefinition = new StageDefinition();
		stageDefinition.setName(attributes.getValue(ATTRIBUTE_NAME));
		currentStage.set(stageDefinition);
	}
	
	private void _parse_property_(Attributes attributes)
	{
		if(currentProperty.get() != null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		Property property = new Property();
		property.setName(attributes.getValue(ATTRIBUTE_NAME));
		String value = attributes.getValue(ATTRIBUTE_VALUE);
		if(value == null)
		{
			property.setType(Property.PropertyTypeEnum.MULTI_VALUES);
		}
		else
		{
			property.setValue(value);
		}
		
		currentProperty.set(property);
	}
	
	private void _parse_value_(Attributes attributes)
	{
		if(currentProperty.get() == null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		currentProperty.get().addValue(attributes.getValue(ATTRIBUTE_VALUE));
	}
	
	private void _parse_dispatcher_(Attributes attributes)
	{
		if(currentDispatcher.get() != null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		DispatcherDefinition dispatcherDefinition = new DispatcherDefinition();
		dispatcherDefinition.setName(attributes.getValue(ATTRIBUTE_NAME));
		
		currentDispatcher.set(dispatcherDefinition);
	}

	private void _parse_router_entry_(Attributes attributes)
	{
		if(currentDispatcher.get() == null)
		{
			throw new RuntimeException("Wrong application config file.");
		}
		
		RouterEntryDefinition routerEntryDefinition = new RouterEntryDefinition();
		routerEntryDefinition.setState(Integer.valueOf(attributes.getValue(ROUTER_ENTRY_ATTRIBUTE_STATE)));
		routerEntryDefinition.setStageName(attributes.getValue(ROUTER_ENTRY_ATTRIBUTE_STAGE));
		
		currentDispatcher.get().addRouterEntry(routerEntryDefinition);
	}
	
	public static void main(String[] args)
	{
		XMLApplicationParser parser = new XMLApplicationParser();
		ApplicationDefinition appDefinition = parser.parse(Thread.currentThread().getContextClassLoader().getResource("application.xml").getFile());
		System.out.println(appDefinition);
	}
}
